/*
    kOTF Open Trace Format - a library for generating execution traces.
    Copyright 2012 Daniel K. O.

    kOTF is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    kOTF is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with kOTF.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef KOTF_STREAM_H
#define KOTF_STREAM_H

#include <kotf/Types.hpp>
#include <kotf/Events.hpp>

#include <kotf/Records.hpp>


namespace kotf {

    class Session;
    class SessionImpl;
    class StreamImpl;

    /** \brief execution stream.
       
        This class represents an execution stream (thread, process, etc). None of its
        methods are thread-safe. You should call the methods of a Stream object only from one
        thread at a time.

        Each Stream object will create an output file named <sessionname>-<streamname>.stream.

        \note No consistency check is performed; the data is only written straight to the
        output file. To avoid mismatching state push/pop you should use the helper classes
        kotf::Scope, kotf::ScopedLock, kotf::Condition whenever possible.

        Each operation (start/finish, push/pop/change, send/broacast/recv) allows for optional
        tags that can have special meaning in an analysis tool.
     */
    class Stream {

        Stream(const Stream&); // do not allow copy construction
        
        StreamImpl *pimpl;
        
        friend class SessionImpl;

    private:
        
        Stream(Session& s, uint64_t i, const string& n);

    public:
        virtual ~Stream(); // note: this does not flush the pending events

        uint64_t getId() const;
        const string& getName() const;
        Session& getSession();

        void setLimit(size_t limit);
        size_t getLimit() const;


        bool isActive() const;

        void start(uint64_t tag=0); ///< marks the beginning of the stream
        void start(const Stream& parent, uint64_t tag=0); ///< marks the beginning of the stream and its parent
        void finish(uint64_t tag=0); ///< marks the end of the stream

        void push(const State& st, uint64_t tag=0); ///< enters a state
        void pop(const State& st, uint64_t tag=0); ///< leaves a state
        void change(const State& oldst, const State& newst, uint64_t tag=0); ///< pops oldst and pushes newst instantly

        void push(const string& stn, uint64_t tag=0); ///< enters a state, using the state name
        void pop(const string& stn, uint64_t tag=0); ///< leaves a state, using the state name
        void change(const string& oldstn, const string& newstn, uint64_t tag=0); ///< pops oldst and pushes newst instantly, using the state names

        void update(const Counter& c, uint64_t v, uint64_t tag=0); ///< updates the counter c to the value v
        void update(const string& cn, uint64_t v, uint64_t tag=0); ///< updates the counter c to the value v, using the counter name

        void send     (const Channel& ch, uint64_t length=0, uint64_t tag=0); ///< sends a message to channel ch
        void broadcast(const Channel& ch, uint64_t length=0, uint64_t tag=0); ///< broadcasts a message to channel ch
        void recv     (const Channel& ch, uint64_t length=0, uint64_t tag=0); ///< receives a message from channel ch

        void send     (const string& chn, uint64_t length=0, uint64_t tag=0); ///< sends a message to channel ch, using the channel name
        void broadcast(const string& chn, uint64_t length=0, uint64_t tag=0); ///< broadcasts a message to channel ch, using the channel name
        void recv     (const string& chn, uint64_t length=0, uint64_t tag=0); ///< receives a message from channel ch, using the channel name


        /** \brief creates or returns a stream for this thread.

            The stream is constructed from the default session, and it receives a start() right away.

            \sa Session::getDefault()
         */
        static Stream& mine();

        
        /** \brief creates or returns a stream for this thread.

            The stream is constructed from the given session, and it receives a start() right away.
         */
        static Stream& mine(Session& s);

        
        /** \brief sets the name that will be used to identify this stream.

            By default a stream's name will be its thread id (which will often look like a\
            hexadecimal number). Use this function to give this thread some arbitrary (and more
            meaningful) name.

            If you create the current thread's stream (by calling Stream::mine())
            and later call setName(), the previous stream discarded and a new one is created (with
            the new name). The old stream will be finish()ed at the session's destructor if
            you don't do it first.
        */
        static void setName(const string& name);


    };

}


#endif
